/**
 * CatalogTree.java 7 avr. 2006
 * 
 * Copyright (c) 2006 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights
 * Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.windu2b.jcaddie.swing;

import java.awt.Component;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;

import org.windu2b.jcaddie.model.*;
import org.windu2b.jcaddie.viewcontroller.*;

/**
 * @author Emmanuel Puybaret, windu.2b
 * 
 */
public class CatalogTree extends JTree implements View
{

	/**
	 * 
	 */
	private static final long	  serialVersionUID	= -3089918640089271868L;


	private TreeSelectionListener	treeSelectionListener;




	/**
	 * Creates a tree that displays <code>catalog</code> content.
	 */
	public CatalogTree( Catalog catalog )
	{
		this( catalog, null );
	}




	/**
	 * Creates a tree controlled by <code>controller</code> that displays
	 * <code>catalog</code> content.
	 */
	public CatalogTree( Catalog catalog, CatalogController controller )
	{
		setModel( new CatalogTreeModel( catalog ) );
		setRootVisible( false );
		setShowsRootHandles( true );
		DefaultTreeCellRenderer renderer = new CatalogCellRenderer();
		setCellRenderer( renderer );
		/*
		 * renderer.setOpenIcon(cata); renderer.setClosedIcon(customClosedIcon);
		 * renderer.setLeafIcon(customLeafIcon);
		 */

		if ( controller != null )
		{
			addSelectionListeners( catalog, controller );
			addMouseListener( controller );
			//addRightClickListener( this );
		}
		setDragEnabled( true );
	}




	/*private void addRightClickListener( final JTree tree )
	{
		tree.addMouseListener( new MouseAdapter()
		{
			public void mousePressed( MouseEvent e )
			{
				if ( e.getButton() == MouseEvent.BUTTON3 )
				{
					// System.out.println("click Right");
					int selRow = tree.getRowForLocation( e.getX(), e.getY() );
					TreePath selPath = tree.getPathForLocation( e.getX(), e
					        .getY() );
					if ( selRow != -1 )
					{
						tree.clearSelection();
						tree.setSelectionPath( selPath );
						System.out.println("Objet : "+ getLastSelectedPathComponent() );
						// Affichage du popup adéquat
					}
				}
			}
		} );
	}*/




	/**
	 * Adds selection listeners to this tree.
	 */
	private void addSelectionListeners( final Catalog catalog,
	        final CatalogController controller )
	{
		final SelectionListener catalogSelectionListener = new SelectionListener()
		{
			public void selectionChanged( SelectionEvent ev )
			{
				getSelectionModel().removeTreeSelectionListener(
				        treeSelectionListener );

				clearSelection();
				updateTreeSelectedProduct( catalog );

				for ( Object item : ev.getSelectedItems() )
				{
					selectPieceOfProduct( catalog,
					        ( CatalogPieceOfProduct ) item );
				}

				getSelectionModel().addTreeSelectionListener(
				        treeSelectionListener );
			}
		};

		treeSelectionListener = new TreeSelectionListener()
		{
			public void valueChanged( TreeSelectionEvent ev )
			{
				catalog.removeSelectionListener( catalogSelectionListener );

				controller.setSelectedProduct( getSelectedProduct() );

				catalog.addSelectionListener( catalogSelectionListener );
			}
		};
		getSelectionModel().addTreeSelectionListener( treeSelectionListener );
		catalog.addSelectionListener( catalogSelectionListener );
	}




	/**
	 * Updates selected nodes in tree from <code>catalog</code> selected
	 * product.
	 */
	private void updateTreeSelectedProduct( Catalog catalog )
	{
		for ( CatalogPieceOfProduct selectedPiece : catalog
		        .getSelectedProduct() )
		{
			// Search the parent category of selectedPiece
			for ( Category category : catalog.getCategories() )
			{
				for ( CatalogPieceOfProduct piece : category.getProduct() )
				{
					// Don't use list methods to search to avoid equality
					// between pieces with same name
					if ( piece == selectedPiece )
					{
						TreePath path = new TreePath( new Object[] { catalog,
						        category, piece } );
						addSelectionPath( path );
						scrollRowToVisible( getRowForPath( path ) );
						break;
					}
				}
			}
		}
	}




	/**
	 * Adds a double click mouse listener to modify selected product.
	 */
	private void addMouseListener( final CatalogController controller )
	{
		addMouseListener( new MouseAdapter()
		{
			@Override
			public void mouseClicked( MouseEvent ev )
			{
				if ( ev.getClickCount() == 1 )
				{
					System.out
					        .println( "Simple-clic dans CatalogTree.addMouseListener()" );
				}
				else if ( ev.getClickCount() == 2 )
				{
					// TODO gérer la consultation/modification via un
					// double-clic
					System.out
					        .println( "Double-clic dans CatalogTree.addMouseListener()" );

					// On récupère le dernier noeud selectionné
					Object node = getLastSelectedPathComponent();

					if ( node instanceof CatalogPieceOfProduct )
					{
						controller.modifySelectedCategory();
						System.out.println( "node =>" + node );
					}
				}
			}
		} );
	}




	private void selectPieceOfProduct( Catalog catalog,
	        CatalogPieceOfProduct selectedPiece )
	{
		for ( Category category : catalog.getCategories() )
		{
			for ( CatalogPieceOfProduct piece : category.getProduct() )
			{
				if ( piece == selectedPiece )
				{
					TreePath path = new TreePath( new Object[] { catalog,
					        category, piece } );
					addSelectionPath( path );
					scrollRowToVisible( getRowForPath( path ) );
					break;
				}
			}
		}
	}




	/**
	 * Returns the selected product in tree.
	 */
	private List<CatalogPieceOfProduct> getSelectedProduct()
	{
		List<CatalogPieceOfProduct> selectedProduct = new ArrayList<CatalogPieceOfProduct>();
		TreePath[] selectionPaths = getSelectionPaths();
		if ( selectionPaths != null )
		{
			for ( TreePath path : selectionPaths )
			{
				if ( path.getPathCount() == 3 )
				    selectedProduct.add( ( CatalogPieceOfProduct ) path
				            .getLastPathComponent() );
			}
		}

		return selectedProduct;
	}



	/**
	 * Cell renderer for this catalog tree.
	 */
	private class CatalogCellRenderer extends DefaultTreeCellRenderer
	{
		/**
		 * 
		 */
		private static final long	serialVersionUID	= -3428762219470826398L;


		private static final int	DEFAULT_ICON_WEIGHT	= 32;




		@Override
		public Component getTreeCellRendererComponent( JTree tree,
		        Object value, boolean selected, boolean expanded, boolean leaf,
		        int row, boolean hasFocus )
		{
			JLabel label = ( JLabel ) super.getTreeCellRendererComponent( tree,
			        value, selected, expanded, leaf, row, hasFocus );

			if ( value instanceof Category )
			{
				label.setText( ( ( Category ) value ).getName() );
				label
				        .setIcon( getLabelIcon( ( ( Category ) value )
				                .getIcon() ) );
			}
			else if ( value instanceof CatalogPieceOfProduct )
			{
				CatalogPieceOfProduct produit = ( CatalogPieceOfProduct ) value;
				label.setText( produit.getName() );
				// label.setIcon( getLabelIcon( produit.getIcon() ) );
			}

			return label;
		}




		/**
		 * Returns an Icon instance with the read image scaled at the tree row
		 * height or an empty image if the image couldn't be read.
		 * 
		 * @param content
		 *            the content of an image.
		 */
		private Icon getLabelIcon( Content content )
		{
			int rowHeight = isFixedRowHeight() ? getRowHeight()
			        : DEFAULT_ICON_WEIGHT;

			return IconManager.getInstance().getIcon( content, rowHeight, this );
		}
	}

	/**
	 * Tree model adaptor to Catalog / Category / PieceOfProduct classes.
	 */
	private static class CatalogTreeModel implements TreeModel
	{
		private Catalog	catalog;




		public CatalogTreeModel( Catalog catalog )
		{
			this.catalog = catalog;
		}




		/**
		 * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
		 */
		public Object getChild( Object parent, int index )
		{
			if ( parent instanceof Catalog )
				return ( ( Catalog ) parent ).getCategories().get( index );
			else return ( ( Category ) parent ).getProduct().get( index );
		}




		/**
		 * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
		 */
		public int getChildCount( Object parent )
		{
			if ( parent instanceof Catalog )
				return ( ( Catalog ) parent ).getCategories().size();
			else return ( ( Category ) parent ).getProduct().size();
		}




		/**
		 * @see javax.swing.tree.TreeModel#getRoot()
		 */
		public Object getRoot()
		{
			return catalog;
		}




		/**
		 * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object,
		 *      java.lang.Object)
		 */
		public int getIndexOfChild( Object parent, Object child )
		{
			if ( parent instanceof Catalog )
				return Collections.binarySearch( ( ( Catalog ) parent )
				        .getCategories(), ( Category ) child );
			else return Collections.binarySearch( ( ( Category ) parent )
			        .getProduct(), ( CatalogPieceOfProduct ) child );
		}




		/**
		 * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
		 */
		public boolean isLeaf( Object node )
		{
			return node instanceof CatalogPieceOfProduct;
		}




		/**
		 * @see javax.swing.tree.TreeModel#valueForPathChanged(javax.swing.tree.TreePath,
		 *      java.lang.Object)
		 */
		public void valueForPathChanged( TreePath path, Object newValue )
		{
		}




		/**
		 * @see javax.swing.tree.TreeModel#addTreeModelListener(javax.swing.event.TreeModelListener)
		 */
		public void addTreeModelListener( TreeModelListener listener )
		{
		}




		/**
		 * @see javax.swing.tree.TreeModel#removeTreeModelListener(javax.swing.event.TreeModelListener)
		 */
		public void removeTreeModelListener( TreeModelListener listener )
		{
		}
	}
}